Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple sources #2528

Merged
merged 3 commits into from
Sep 9, 2021
Merged

Conversation

davedawkins
Copy link
Contributor

This PR provides additional support for REPL "projects" of multiple source FS files (as used by the Sutil REPL - a separate PR will be created against the main REPL for multiple file support).

Accepting this PR should not affect the main Fable REPL (this has been tested against a local build of the main REPL). The affected build files are worker.min.js (and possibly bundle.min.js).

This PR defines the following additional WorkerRequest messages:

    | ParseFile of file : string * fsharpCode: FSharpCodeFile[] * otherFSharpOptions: string[]
    | CompileFiles of fsharpCode: FSharpCodeFile[] * otherFSharpOptions: string[]
    | GetTooltipForFile of id: Guid * file: string * line: int * column: int * lineText: string
    | GetCompletionsForFile of id: Guid * file: string * line: int * column: int * lineText: string
    | GetDeclarationLocationForFile of id: Guid * file: string * line: int * column: int * lineText: string

and one additional WorkerAnswer message:

    | CompilationsFinished of jsCode: string[] * errors: Fable.Standalone.Error[] * stats: CompileStats

Existing messages are not changed; this means that the resulting worker.min.js will work with the existing Fable REPL (this has been tested).

…trict extension. This will keep worker.min.js compatible with the main Fable repl
@MangelMaxime
Copy link
Member

Hello @davedawkins,

does it means it would be possible to copy the code of library on the fly and get intellisense for it?

I am asking because currently, Fable REPL as the limit of not supporting inline function in pre-compiled library. So if you we could compile the library on the fly, this could remove this limitation.

@davedawkins
Copy link
Contributor Author

davedawkins commented Sep 7, 2021

does it means it would be possible to copy the code of library on the fly and get intellisense for it?

I am asking because currently, Fable REPL as the limit of not supporting inline function in pre-compiled library. So if you we could compile the library on the fly, this could remove this limitation.

Interesting!

I tried an experiment in the Sutil REPL.

  • Compile and run -> working
  • Tooltips -> working
  • Intellisense -> not working

This may be to do with the way I am presenting the files for compilation in the "ParseFiles" message

image

Here is the other file with the inline definitions:

image

With regard to Intellisense, I'm also seeing this:
image

So, I think that the "ParseFiles" implementation in Worker.cs might need some work (I will check to make sure it's not my REPL tooltip provider doing this).

One issue I'm aware of: if we have project files [ "Main.fs"; "Util.fs" ], then when we activate parsing for "Util.fs" (to support tooltips/intellisense) we still send all the project files over and so we compile "Main.fs" unnecessarily (though there may be caching that helps us out there). This could be fixed from the REPL side.

@davedawkins
Copy link
Contributor Author

davedawkins commented Sep 7, 2021

Ah! I know why there are duplicate entries in the menu. I had the same issue with tooltips. Hidden editors still react to window events, so I have to filter on editor.isHidden in the tooltip/completion providers. This is a fix for the REPL, not the Worker

@davedawkins
Copy link
Contributor Author

I fixed the duplicate entries, and now I see that intellisense is also working!

image

So - I think your idea could work. It would be interesting to see what the performance is like though.

@MangelMaxime
Copy link
Member

Awesome @davedawkins 🎉

Indeed, we will need to check the performance implication. About the performance, if the result are cached after the first compilation, it would "just" mean an additional cost when loading the REPL or activating support for a certain library which should be ok.

In theory, it can open a lot of possibilities.

My idea, is to let the user choose which library is activated like other REPL do. You select the library from a predefined list, which then add the library file in the background (hidden/non-editable from the user view).

Ah! I know why there are duplicate entries in the menu. I had the same issue with tooltips. Hidden editors still react to window events, so I have to filter on editor.isHidden in the tooltip/completion providers. This is a fix for the REPL, not the Worker

I suppose this is because each tab is loaded it's own instance of Monaco. I suppose, we could rework the code to reuse the same instance and just update the text content when switching between the tabs. I am just not sure about the implication of either solution.

@davedawkins
Copy link
Contributor Author

The library selector would be a superb addition to the REPL. I think also that this approach would then allow Feliz to be included (since I understand it makes heavy use of inlines)

I suppose this is because each tab is loaded it's own instance of Monaco. I suppose, we could rework the code to reuse the same instance and just update the text content when switching between the tabs. I am just not sure about the implication of either solution.

It feels like an editor should stop listening when it is display:none. I don't know enough about Monaco (yet) to find the "right" way to hide it. Currently we just hide the top-level container div. Yes, we could separate the tab headers from the editor instance. I could try that too.

@MangelMaxime
Copy link
Member

The library selector would be a superb addition to the REPL. I think also that this approach would then allow Feliz to be included (since I understand it makes heavy use of inlines)

Indeed Feliz use inline and Erased heavily to reduce the bundle size.

And in theory, it should allow it. Same for Thoth.Json it should allow to support 100% of the API as there is a small portion of it which is not supported today (related to auto coders).

@ncave
Copy link
Collaborator

ncave commented Sep 7, 2021

@davedawkins This is fantastic, so nice to see a REPL that supports multiple source files, ever since the support for it was added a few years back in #1714. Thank you! 🚀🎉

we still send all the project files over and so we compile "Main.fs" unnecessarily (though there may be caching that helps us out there)

Yes, there is caching, so there should be no unnecessary compilation, but there is the normal space vs time trade-off as with any caching (i.e. memory cost). All sources (above the file that needs to be compiled) have to be sent every time, but only the ones that actually changed (and everything below that, even if unchanged) will be compiled.

It's a simple scheme, and there is probably room for improvement, but I don't think it matters much for performance, as hashing is cheap relative to everything else that FCS does. For example Fable compiler controls the source version externally, but I personally find it a bit simpler from API perspective to just send in the sources and let the compiler figure it out, as they can be hashed and looked up in the cache if they hadn't changed.

@davedawkins
Copy link
Contributor Author

@davedawkins This is fantastic, so nice to see a REPL that supports multiple source files, ever since the support for it was added a few years back in #1714. Thank you! 🚀🎉

:-) You can see it in action here https://sutil.dev/repl. I'm glad you like the idea, and I appreciate the comments.

All sources (above the file that needs to be compiled) have to be sent every time, but only the ones that actually changed (and everything below that, even if unchanged) will be compiled.

I think you're saying that:

  • I should consider not sending the higher-level files (eg, don't send "Main.fs" when compiling/parsing "Util.fs" where Main.fs depends on "Util.fs") (interpreting what you mean by "above" - you might mean "other than")
  • Other than that, don't worry about sending all the sources each time, trust the cache.

@ncave
Copy link
Collaborator

ncave commented Sep 7, 2021

@davedawkins

I should consider not sending the higher-level files

No need, the compiler will do that for you.

@alfonsogarciacaro
Copy link
Member

Thanks a lot for this @davedawkins! Yes, it'd be nice to use this work to add libraries on the fly to the REPL. As @ncave says, the compiler should be able to cache unchanged files, but given that library code is not expected to be touched by the user, maybe we can even release the memory after the first compilation and keep only the bits Fable needs (AST for inlined functions and root modules, besides symbol/entity info which is already in the .dll). This is also being discussed in #2527 so it'd be great to have something that helps both Fable CLI and the REPL :)

@alfonsogarciacaro alfonsogarciacaro merged commit da3993b into fable-compiler:main Sep 9, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants